package com.hzncc.flowable_diboot.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.diboot.core.util.JSON;
import com.diboot.core.util.S;
import com.diboot.core.vo.JsonResult;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.hzncc.flowable_diboot.entity.DingAppConfig;
import com.hzncc.flowable_diboot.entity.DingDingGroup;
import com.hzncc.flowable_diboot.enums.MsgKey;
import com.hzncc.flowable_diboot.service.DingAppConfigService;
import com.hzncc.flowable_diboot.service.DingtalkService;
import com.taobao.api.ApiException;
import com.taobao.api.FileItem;
import lombok.extern.log4j.Log4j2;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

@Service
@Log4j2
public class DingtalkServiceImpl implements DingtalkService {

    //private RedisTemplate redisTemplate;

    @Value("${dingding.appKey}")
    private String appKey;
    @Value("${dingding.appSecret}")
    private String appSecret;
    @Value("${dingding.robotCode}")
    private String robotCode;
    @Value("${dingding.agentId}")
    private Long agentId;
    @Value("${dingding.groupTemplateId}")
    private String groupTemplateId;
    @Value("${dingding.groupRobotCode}")
    private String groupRobotCode;

    @Autowired
    private DingAppConfigService dingAppConfigService;

    private static final Cache<String, String> CACHE = CacheBuilder.newBuilder().expireAfterWrite(Duration.ofSeconds(7000)).build();
    private static final String TOKEN_NAME = "ding-token";


    /**
     * 获取client
     *
     * @return
     * @throws Exception
     * @author 陈广煜
     * @date 2024/6/12
     */
    public static Client createClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new Client(config);
    }

    /**
     * 获取钉钉令牌，优先从本地缓存中获取，若不存在或已过期则从钉钉服务器获取
     * 企业内部应用token
     *
     * @return 包含令牌的 JsonResult
     * @throws Exception 当获取令牌出现异常时抛出
     */
    @Override
    public JsonResult<String> getToken(String tenantId) throws Exception {
        // 获取本地缓存token
        String token = CACHE.getIfPresent(TOKEN_NAME + tenantId);
        if (S.isNotBlank(token)) {
            return JsonResult.OK(token);
        }
//        if (tenantId != null) {
//            DingAppConfig appConfig = getAppConfig(tenantId);
//            this.appKey = appConfig.getAppKey();
//            this.appSecret = appConfig.getAppSecret();
//            this.agentId = getAgentId();
//            this.robotCode = appConfig.getRobotCode();
//            this.groupTemplateId = appConfig.getGroupTemplateId();
//            this.groupRobotCode = appConfig.getGroupRobotCode();
//        }
        Client client = createClient();
        GetAccessTokenRequest getAccessTokenRequest = new GetAccessTokenRequest()
                .setAppKey(appKey)
                .setAppSecret(appSecret);
        try {
            GetAccessTokenResponse accessToken = client.getAccessToken(getAccessTokenRequest);
            token = accessToken.getBody().getAccessToken();
            CACHE.put(TOKEN_NAME + tenantId, token);
            // 缓存token 钉钉token过期时间7200秒
            //redisTemplate.opsForValue().setIfAbsent("dingtalk-token",token,7200, TimeUnit.SECONDS);
        } catch (TeaException err) {
            log.error(err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                return JsonResult.FAIL_EXCEPTION(err.message);
            }
        } catch (Exception _err) {
            log.error(_err);
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                return JsonResult.FAIL_EXCEPTION(err.message);
            }
        }
        return JsonResult.OK(token);
    }

    public JsonResult<String> getToken() throws Exception {
        return getToken(null);
    }

    public JsonResult<String> getToken(String mAppKey, String mAppSecret) throws Exception {
        // 获取本地缓存token
        String token = CACHE.getIfPresent(TOKEN_NAME + mAppKey);
        if (S.isNotBlank(token)) {
            return JsonResult.OK(token);
        }
        Client client = createClient();
        GetAccessTokenRequest getAccessTokenRequest = new GetAccessTokenRequest()
                .setAppKey(mAppKey)
                .setAppSecret(mAppSecret);
        try {
            GetAccessTokenResponse accessToken = client.getAccessToken(getAccessTokenRequest);
            token = accessToken.getBody().getAccessToken();
            CACHE.put(TOKEN_NAME + mAppKey, token);
            // 缓存token 钉钉token过期时间7200秒
            //redisTemplate.opsForValue().setIfAbsent("dingtalk-token",token,7200, TimeUnit.SECONDS);
        } catch (TeaException err) {
            log.error(err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                return JsonResult.FAIL_EXCEPTION(err.message);
            }
        } catch (Exception _err) {
            log.error(_err);
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                return JsonResult.FAIL_EXCEPTION(err.message);
            }
        }
        return JsonResult.OK(token);
    }

    @Override
    public JsonResult<List<OapiCheckinRecordGetResponse.CheckinRecordVo>> getCheckInData(String token, OapiCheckinRecordGetRequest req) throws ApiException {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/checkin/record/get");
        OapiCheckinRecordGetResponse rsp = client.execute(req, token);
        if (!Objects.equals(rsp.getErrorCode(), "0")) {
            return JsonResult.FAIL_EXCEPTION(JSON.toJSONString(rsp));
        }
        return JsonResult.OK(rsp.getResult().getPageList());
    }

    /**
     * 创建群聊
     *
     * @param group 群聊信息，包含群名和群主
     * @param users 群成员列表
     * @return 创建成功后群的 conversationId,或错误信息
     * @throws Exception
     */
    @Override
    public JsonResult<?> createChat(String token, DingDingGroup group, List<String> users) throws Exception {
        // 添加robot到群聊
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/chat/create");
        OapiChatCreateRequest req = new OapiChatCreateRequest();
        req.setName(group.getName());
        req.setOwner(group.getManager());
        req.setUseridlist(users);
        req.setShowHistoryType(1L);
        req.setSearchable(0L);
        req.setValidationType(0L);
        req.setMentionAllAuthority(0L);
        req.setManagementType(0L);
        req.setChatBannedType(0L);
        OapiChatCreateResponse rsp = client.execute(req, token);
        String errorCode = rsp.getErrorCode();
        if (!S.equals("0", errorCode)) {
            return JsonResult.FAIL_EXCEPTION(rsp.getErrmsg());
        }
        group.setOpenConversationId(rsp.getOpenConversationId());
        return JsonResult.OK(group.getOpenConversationId());
    }

    /**
     * 创建模板群聊
     *
     * @return
     * @throws Exception
     */
    @Override
    public JsonResult<String> createTemplateChat(String token, DingDingGroup group) throws Exception {

        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/im/chat/scenegroup/create");
        OapiImChatScenegroupCreateRequest req = new OapiImChatScenegroupCreateRequest();
        req.setTemplateId(groupTemplateId);
        req.setTitle(group.getName());
        req.setOwnerUserId(group.getManager());
        req.setUuid(UUID.randomUUID().toString());

        OapiImChatScenegroupCreateResponse rsp = client.execute(req, token);
        String errorCode = rsp.getErrorCode();
        if (!S.equals("0", errorCode)) {
            return JsonResult.FAIL_EXCEPTION(rsp.getErrmsg());
        }
        group.setOpenConversationId(rsp.getResult().getOpenConversationId());
        return JsonResult.OK(group.getOpenConversationId());
    }

    @Override
    public JsonResult<String> sendRobotMessage(String token, String openConversationId, String message) throws Exception {
        return sendRobotMessage(token, openConversationId, message, MsgKey.SAMPLE_MARKDOWN.getKey());
    }

    /**
     * 发送群聊消息
     *
     * @param openConversationId
     * @param message
     * @return
     * @author 陈广煜
     * @date 2024-08-27
     */
    @Override
    public JsonResult<String> sendTemplateRobotMessage(String token, String openConversationId, String message, String type) {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/im/chat/scencegroup/message/send_v2");
        OapiImChatScencegroupMessageSendV2Request req = new OapiImChatScencegroupMessageSendV2Request();
        req.setTargetOpenConversationId(openConversationId);
        req.setMsgTemplateId("inner_app_template_text");
//        LocalDate localDate = LocalDate.now().minusDays(1);
//        Map<Object, Object> map = MapUtil.builder().put("title", localDate).put("markdown_content", message).build();
        req.setMsgParamMap(message);
        req.setRobotCode(groupRobotCode);
        OapiImChatScencegroupMessageSendV2Response rsp = null;
        try {
            rsp = client.execute(req, token);
            System.out.println(rsp.getBody());
        } catch (ApiException e) {
            throw new RuntimeException(e);
        }
        return JsonResult.OK();
    }

    @Override
    public JsonResult<String> uploadFile(String token, String filePath, String fileType) throws Exception {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/media/upload");
        OapiMediaUploadRequest req = new OapiMediaUploadRequest();
        req.setType(fileType);
        // 要上传的媒体文件
        FileItem item = new FileItem(filePath);
        req.setMedia(item);
        OapiMediaUploadResponse rsp = client.execute(req, token);
        return JsonResult.OK(rsp.getMediaId());
    }

    @Override
    public JsonResult<String> sendRobotMessage(String token, String openConversationId, String message, String type) throws Exception {
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
        config.protocol = "https";
        config.regionId = "central";
        com.aliyun.dingtalkrobot_1_0.Client client = new com.aliyun.dingtalkrobot_1_0.Client(config);
        com.aliyun.dingtalkrobot_1_0.models.OrgGroupSendHeaders orgGroupSendHeaders = new com.aliyun.dingtalkrobot_1_0.models.OrgGroupSendHeaders();
        orgGroupSendHeaders.xAcsDingtalkAccessToken = token;
        com.aliyun.dingtalkrobot_1_0.models.OrgGroupSendRequest orgGroupSendRequest = new com.aliyun.dingtalkrobot_1_0.models.OrgGroupSendRequest()
                .setMsgParam(message)
                .setMsgKey(type)
                .setOpenConversationId(openConversationId)
                .setRobotCode(robotCode);
        try {
            client.orgGroupSendWithOptions(orgGroupSendRequest, orgGroupSendHeaders, new com.aliyun.teautil.models.RuntimeOptions());
        } catch (TeaException err) {
            JsonResult<String> result = getJsonResult(err);
            if (result != null) {
                return result;
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            JsonResult<String> result = getJsonResult(err);
            if (result != null) {
                return result;
            }
        }
        return JsonResult.OK();
    }

    /**
     * 获取错误信息
     *
     * @param err
     * @return
     */
    @Nullable
    private JsonResult<String> getJsonResult(TeaException err) {
        if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
            // err 中含有 code 和 message 属性，可帮助开发定位问题
            if (S.equals("88", err.code)) {
                CACHE.invalidate(TOKEN_NAME);
                CACHE.cleanUp();
            }
            log.error(err.message);
            JsonResult<String> result = new JsonResult<>();
            result.setData(err.code);
            result.setMsg(err.message);
            result.setCode(-1);
            return result;
        }
        return null;
    }


    // 发送钉钉工作通知
    public JsonResult<String> sendWorkNotice(String token, String userId, Map<String, Object> param) throws Exception {
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2");
        OapiMessageCorpconversationAsyncsendV2Request req = new OapiMessageCorpconversationAsyncsendV2Request();
        req.setUseridList(userId);
        req.setAgentId(getAgentId());
        req.setToAllUser(false);
        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();

        Object msgtype = param.get("msgtype");
        // todo msgtype
        switch (msgtype.toString()) {
            case "file" -> {
                msg.setFile(new OapiMessageCorpconversationAsyncsendV2Request.File());
                msg.getFile().setMediaId(param.get("media_id").toString());
            }
            case "image" -> {
            }
            case "markdown" -> {

            }
            case "oa" -> {}
            default -> {
                msg.setText(new OapiMessageCorpconversationAsyncsendV2Request.Text());
                msg.getText().setContent(param.get("content").toString());
            }
        }
        msg.setMsgtype((String) msgtype);
        req.setMsg(msg);
        OapiMessageCorpconversationAsyncsendV2Response rsp = client.execute(req, token);
        return JsonResult.OK(rsp.getCode());
    }

    /**
     * 获取钉钉agentId
     *
     * @return
     * @author 陈广煜
     * @date 2024-08-15
     */
    public Long getAgentId() throws ApiException {
        return agentId;
    }

    /**
     * 根据租户获取钉钉配置
     *
     * @param tenantId
     * @return
     * @author 陈广煜
     * @date 2024-08-28
     */
    public DingAppConfig getAppConfig(String tenantId) {
        QueryWrapper<DingAppConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(DingAppConfig::getTenant, tenantId);
        return dingAppConfigService.getSingleEntity(queryWrapper);
    }

    /**
     * 根据dingAuthCode获取dingding用户id
     *
     * @param dingAuthToken
     * @param tenantId
     * @return
     * @throws ApiException
     */
    @Override
    public String getUserInfoByCode(String dingAuthToken, String tenantId) throws ApiException {
        DingAppConfig appConfig = getAppConfig(tenantId);
        JsonResult<String> token = null;
        try {
            token = getToken(appConfig.getMAppKey(), appConfig.getMAppSecret());
        } catch (Exception e) {
            throw new RuntimeException("获取token异常");
        }
        if (token.getCode() == 0) {
            DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/getuserinfo");
            OapiV2UserGetuserinfoRequest req = new OapiV2UserGetuserinfoRequest();
            req.setCode("bab02f63c1e030fbbxxxx");
            OapiV2UserGetuserinfoResponse rsp = client.execute(req, token.getData());
            return rsp.getResult().getUnionid();
        }
        return null;
    }
}
